import numpy as np

class Smooth:
    @staticmethod
    def smooth1(signal, window_len):

        """计算信号平滑后的结果

        :param signal: np数据
        :param window_len: 整数（时间窗口）
        :return: pd数据
        """

        if window_len // 2 == 0:  # 偶数转奇数
            window_len -= 1
        length = len(signal)
        y = np.zeros(length)
        N = (window_len - 1) / 2
        for i in range(0, length):
            cont_0 = i
            cont_end = length - i - 1
            if cont_0 in range(0, int(N)) or cont_end in range(0, int(N)):
                cont = min(cont_0, cont_end)
                y[i] = np.mean(signal[i - cont: i + cont + 1])
            else:
                y[i] = np.mean(signal[i - int(N): i + int(N) + 1])
        return y

    @staticmethod
    def smooth2(signal, window_len):
        if not window_len % 2:
            if window_len == 0:
                raise Exception("Window length needs to be a positive odd number.")
            else:
                # 窗口宽度需为奇数，若为偶数则默认减1
                window_len = window_len - 1
        if window_len > len(signal):
            raise Exception("Input signal length needs to be bigger than window size.")

        half_win = window_len // 2
        sig_len = len(signal)
        smoothed_signal = np.zeros(sig_len)
        start_critical_index = half_win
        end_critical_index = sig_len - half_win - 1
        count = 0
        for index in range(0, sig_len):
            if index < start_critical_index:
                smoothed_signal[index] = np.mean(signal[0:2 * index + 1])
            elif index > end_critical_index:
                count += 1
                smoothed_signal[index] = np.mean(signal[-window_len + 2 * count:])
            elif start_critical_index <= index <= end_critical_index:
                startInd = max([0, index - half_win])
                endInd = min(index + half_win + 1, sig_len)
                smoothed_signal[index] = np.mean(signal[startInd:endInd])
        return smoothed_signal

    @staticmethod
    def smooth3(signal, window_len):
        if window_len % 2 == 0:  # 偶数转奇数
            window_len -= 1
        N = (window_len - 1) // 2
        sub_sequences = np.lib.stride_tricks.sliding_window_view(signal, window_len)
        means = np.mean(sub_sequences, axis=1)

        # # 方法一：用for循环 稍微慢一点
        # signal_tmp1 = signal[:window_len-2]
        # signal_tmp2 = signal[-window_len+2:]
        # y_tmp1 = [np.mean(signal_tmp1[:2*i+1]) for i in range(N)]
        # y_tmp2 = [np.mean(signal_tmp2[2*i:]) for i in range(N)]

        # 方法二：用累计和函数
        y_tmp_indices = np.arange(0, window_len - 2, 2)
        y_tmp1 = np.cumsum(signal[:window_len-2])[y_tmp_indices] / (np.arange(0, N, 1) * 2 + 1)
        y_tmp2 = np.cumsum(np.flip(signal[-window_len+2:]))[y_tmp_indices] / (np.arange(0, N, 1) * 2 + 1)
        y = np.concatenate((y_tmp1, means, np.flip(y_tmp2)))
        return y